﻿#include "OiPCH.hpp"
#include <QtCharts\QScatterSeries>
#include <QtCharts\QLineSeries>
#include <QtCharts\QSplineSeries>
#include <QtCharts\QValueAxis>
#include <QGraphicsRectItem>

using namespace QtCharts;

namespace Oi
{
    namespace PLScannerCore
    {
        static OiProfiles pointCloudParser(OiProfile contour, double connectionThreshold, double linkingThreshold, double noiseThreshold)
        {
            OiProfiles outputQList;
            OiProfile tempQList;

            for(int i = 0; i < contour.size(); i++)
            {
                if(tempQList.empty() || connectionThreshold <= 0)
                {
                    tempQList.append(contour[i]);
                }
                else
                {
                    if(( contour[i] -  tempQList.last()).manhattanLength() < connectionThreshold)
                    {
                        tempQList.append(contour[i]);
                    }
                    else
                    {
                        if(tempQList.size() > noiseThreshold)
                        {
                            if(outputQList.empty())
                            {
                                outputQList.append(tempQList);
                            }
                            else
                            {
                                if(( ( ( outputQList.last() ).last() ) - tempQList.first() ).manhattanLength() < linkingThreshold)
                                {
                                    outputQList.last() = outputQList.last() + tempQList;
                                }
                                else
                                {
                                    outputQList.append(tempQList);
                                }
                            }
                        }
                        tempQList.clear();
                        tempQList.append(contour[i]);
                    }
                }
            }
            if(tempQList.size() > noiseThreshold)
            {
                if(outputQList.empty())
                {
                    outputQList.append(tempQList);
                }
                else
                {
                    if(( ( ( outputQList.last() ).last() ) - tempQList.first() ).manhattanLength() < linkingThreshold)
                    {
                        outputQList.last() = outputQList.last() + tempQList;
                    }
                    else
                    {
                        outputQList.append(tempQList);
                    }
                }
            }

            return outputQList;
        }

        class ProfilesChartImpl : public Impl<ProfilesChart>
        {
        public:
            QMutex mutex;
        };

        ProfilesChart::ProfilesChart(QGraphicsItem *parent, Qt::WindowFlags wFlags)
            : QChart(parent, wFlags)
        {
            OI_I(ProfilesChart);
            this->setTheme((ChartTheme)PLScannerStyle::ChartDefaultTheme());
            this->setBackgroundVisible(false);

            this->setPlotAreaBackgroundBrush(PLScannerStyle::ChartBgColor());
            this->setPlotAreaBackgroundVisible(true);
            this->layout()->setContentsMargins(0, 0, 0, 0);
            this->setBackgroundRoundness(0);
        }

        ProfilesChart::~ProfilesChart()
        {
            OI_E(ProfilesChart);
        }

        void ProfilesChart::updateProfiles(const OiProfile& profile, double connectionThreshold, double linkingThreshold, double noiseThreshold)
        {
            auto profiles = pointCloudParser(profile, connectionThreshold, linkingThreshold, noiseThreshold);

            updateProfiles(profiles);
        }

        void ProfilesChart::updateProfiles(const OiProfiles& profiles)
        {
            QMutexLocker lock(&OI_IMPL()->mutex);

            QList<QLineSeries*> lstProfileSeries;

            for(auto s : series())
            {
                if(s->type() == QAbstractSeries::SeriesTypeLine)
                {
                    lstProfileSeries.append((QLineSeries*)s);
                }
            }

            for(int i = 0; i < profiles.size(); i++)
            {
                if(i < lstProfileSeries.size())
                {
                    lstProfileSeries[i]->replace(profiles[i]);
                }
                else
                {
                    QLineSeries* newContour = new QLineSeries();
                    newContour->append(profiles[i]);
                    addSeries(newContour);
                    newContour->attachAxis(this->axisX());
                    newContour->attachAxis(this->axisY());
                }
            }

            for(int i = profiles.size(); i < lstProfileSeries.size(); i++)
            {
                this->removeSeries(lstProfileSeries[i]);
                delete lstProfileSeries[i];
            }

            emit frame(profiles);
        }

        QList<QPointF> ProfilesChart::profiles()
        {
            QMutexLocker lock(&OI_IMPL()->mutex);

            QList<QPointF> profiles;

            for(auto s : series())
            {
                if(s->type() == QAbstractSeries::SeriesTypeLine)
                {
                    profiles.append(((QLineSeries*)s)->points());
                }
            }

            return profiles;

        }

        void ProfilesChart::clearAllSeries()
        {
            QMutexLocker lock(&OI_IMPL()->mutex);

            QList<QAbstractSeries*> lstSeries = this->series();

            for (int i = 0; i < lstSeries.size(); i++)
            {
                this->removeSeries(lstSeries[i]);
                delete lstSeries[i];
            }
        }

        void ProfilesChart::autoRange(double percentage, QPointF center)
        {
            QList<QPointF> points = profiles();

            QPolygonF polygon(points.toVector());
            auto displayRect = polygon.boundingRect();
            auto displayBoxSize = plotArea();

            if (!center.isNull())
            {
                double radiumX = qMax(fabs(displayRect.left() - center.x()), fabs(displayRect.right() - center.x()));
                double radiumY = qMax(fabs(displayRect.top() - center.y()), fabs(displayRect.bottom() - center.y()));;
                displayRect = QRectF(center - QPointF(radiumX, radiumY), center + QPointF(radiumX, radiumY));
            }
            
            double rangeX = displayRect.width() / percentage, rangeY = displayRect.height() / percentage;
            if (displayRect.width() / displayRect.height() >= displayBoxSize.width() / displayBoxSize.height())
            {
                rangeY = rangeX * displayBoxSize.height() / displayBoxSize.width();
            }
            else
            {
                rangeX = rangeY * displayBoxSize.width() / displayBoxSize.height();
            }

            axisX()->setRange(displayRect.center().rx() - rangeX / 2.0, displayRect.center().rx() + rangeX / 2.0);
            axisY()->setRange(displayRect.center().ry() - rangeY / 2.0, displayRect.center().ry() + rangeY / 2.0);
        }
    }

}
